package com.ajavaer.framework.handle.impl;

import com.ajavaer.framework.cache.type.CacheItem;
import com.ajavaer.framework.config.CacheEngineConfig;
import com.ajavaer.framework.handle.CaffeineHandle;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;

import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

public class CaffeineHandleImpl<K, T> implements CaffeineHandle<K, T> {
    private Logger log = LoggerFactory.getLogger(CaffeineHandleImpl.class);
    private DelayQueue<CacheItem> delayQueue = new DelayQueue<>();

    private Cache<K, T> CACHE;

    public CaffeineHandleImpl() {
        CACHE = Caffeine.newBuilder().build();
    }

    @Override
    public void init(CacheEngineConfig cacheEngineConfig, ApplicationContext contextAware) {
        new Thread(() -> {
            try {
                while (!Thread.interrupted()) {
                    CacheItem cacheItem = delayQueue.take();
                    CACHE.invalidate(cacheItem.getKey());
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }).start();
    }

    @Override
    public void put(K key, T value, Integer expire, TimeUnit timeUnit) {
        CacheItem<K, T> cacheItem = new CacheItem<>(key, value, expire, timeUnit);
        CACHE.put(key, value);
        if (expire > 0) {
            delayQueue.add(cacheItem);
        }
    }

    @Override
    public T get(K key, Function<K, CacheItem<K, T>> create) {
        return CACHE.get(key, k -> {
            CacheItem<K, T> cacheItem = create.apply(k);
            if (cacheItem.getExpire() > 0) {
                delayQueue.add(cacheItem);
            }
            return cacheItem.getData();
        });
    }

    @Override
    public int size() {
        return CACHE.asMap().size();
    }

    @Override
    public void delete(K key) {
        CACHE.invalidate(key);
    }

    @Override
    public void clear() {
        CACHE.invalidateAll();
        delayQueue.clear();
    }

    @Override
    public ConcurrentMap<K, T> asMap() {
        return CACHE.asMap();
    }
}
